home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Snippets / Printing / Rotated Thingies / …using Rotate PicComments / Rotn Objs.p < prev    next >
Encoding:
Text File  |  1992-07-15  |  11.8 KB  |  497 lines  |  [TEXT/MPS ]

  1. {**
  2.  **     Program: Rotn Objects
  3.  **        7/25/91
  4.  **
  5.  **        MPW 3.2 Pascal source.
  6.  **
  7.  **     Purpose:
  8.  **
  9.  **        Rotn Objects demonstrates how to rotate objects on PostScript printers
  10.  **        using the RotateBegin/End PicComments.  PrGeneral is used to change
  11.  **        the resolution, which throws another wrench in the works; you have to
  12.  **        scale everything.
  13.  **        
  14.  **        Dave Hersey
  15.  **
  16.  **        Macintosh Developer Technical Support
  17.  **
  18.  **        ----
  19.  **             ver. 1.0    7/25/91        dmh
  20.  **
  21.  **}
  22.  
  23. PROGRAM RotnObjects;
  24.  
  25. USES
  26.     MemTypes, QuickDraw, OSIntf, ToolIntf, Traps, MacPrint, Packages;
  27.  
  28. CONST
  29.  
  30.     {The following constants are used to identify menus and their items. The menu IDs
  31.      have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  32.  
  33.     rMenuBar    = 128;                    {menubar}
  34.  
  35.     mApple        = 128;                    {Apple menu}
  36.     iAbout        = 1;
  37.  
  38.     mFile        = 129;                    {File menu}
  39.     iPrint        = 1;
  40.     iQuit        = 3;
  41.  
  42.     mEdit        = 130;                    {Edit menu}
  43.  
  44.  
  45. VAR
  46.  
  47.     gQuitting    : Boolean;                {"Are we all done?" flag}
  48.  
  49.  
  50. {*------ RotateStuff -----------------------------------------------------------*}
  51.  
  52. {**
  53.  **      RotateStuff draws stuff rotated 45° using the RotateBegin/End PicComments.
  54.  **      The prRsl value passed is the resolution of the printer port.  This value
  55.  **      is used to scale all the graphics (text included) to the printer's
  56.  **      resolution.  We need to do this because we're changing resolution with
  57.  **      PrGeneral.
  58.  **}
  59.  
  60. {$S Main}
  61. PROCEDURE RotateStuff(prRsl : Integer);
  62.  
  63.     CONST
  64.         RotateBegin  = 200;
  65.         RotateEnd     = 201;
  66.         RotateCenter = 202;
  67.  
  68.     TYPE
  69.         rothdl         = ^rotptr;
  70.         rotptr         = ^trot;
  71.  
  72.         trot        = RECORD
  73.                             flip: integer;
  74.                             Angle: integer;
  75.                       END;     { trot }
  76.  
  77.         centhdl         = ^centptr;
  78.         centptr         = ^cent;
  79.         cent         = PACKED RECORD
  80.                             yInt: integer;
  81.                             yFrac: integer;
  82.                             xInt: integer;
  83.                             xFrac: integer;
  84.                       END;     { cent }
  85.  
  86.     VAR
  87.         rotation     : rothdl;
  88.         center        : centhdl;
  89.         where        : Point;
  90.         aRect        : Rect;
  91.         scalar        : Real;
  92.         fNum        : Integer;
  93.  
  94.     BEGIN
  95.         
  96.         scalar := prRsl;
  97.         scalar := scalar / 72;
  98.  
  99.     {Do a dummy draw to set the clipping region.}
  100.  
  101.         PenSize(0, 0);
  102.         MoveTo(0, 0);
  103.         LineTo(0, 0);
  104.         PenSize(1, 1);
  105.  
  106.         rotation := rothdl(NewHandle(SizeOf(trot)));
  107.         rotation^^.flip := 0; {no flip}
  108.         rotation^^.Angle := 45; {45 degree rotation}
  109.  
  110.         where.h := Round(100 * scalar);
  111.         where.v := Round(100 * scalar);
  112.         
  113.         center := centhdl(NewHandle(SizeOf(cent)));
  114.         center^^.xInt := where.h;  {center at where.}
  115.         center^^.yInt := where.v;
  116.         center^^.xFrac := 0;  {no fractional part}
  117.         center^^.yFrac := 0;
  118.         
  119.  
  120.     {Set the center of rotation.}
  121.  
  122.         PicComment(RotateCenter,GetHandleSize(handle(center)),handle(center));
  123.  
  124.     {Begin rotation.}
  125.  
  126.         PicComment(RotateBegin,GetHandleSize(handle(rotation)),handle(rotation));
  127.         MoveTo(where.h, where.v);
  128.  
  129.     {Draw rotated stuff.}
  130.  
  131.         GetFNum('Times', fNum);
  132.         TextFont(fNum);
  133.         TextSize(Round(24 * scalar));
  134.         DrawString('This text is rotated 45°…');
  135.  
  136.         SetRect(aRect, Round(200 * scalar), Round(150 * scalar), Round(300 * scalar), Round(250 * scalar));
  137.         FrameRect(aRect);
  138.  
  139.     {Rotation ends here.}
  140.  
  141.         PicComment(RotateEnd,0,NIL); {RotateEnd}
  142.         DisposHandle(handle(rotation)); {Clean up}
  143.         DisposHandle(handle(center));
  144.  
  145.     END;    {**  RotateStuff  **}
  146.  
  147.  
  148. {*------ DrawStuff -----------------------------------------------------------------*}
  149.  
  150. {**
  151.  **      DrawStuff draws the objects.  prRsl is the resolution of the printer's
  152.  **        GrafPort and is used to determine the amount to scale everything.
  153.  **}
  154.  
  155. {$S Main}
  156.  PROCEDURE DrawStuff(theGPort : GrafPtr; prRsl : Integer);
  157.  
  158.  VAR
  159.     oldPort        : GrafPtr;
  160.  
  161.  BEGIN
  162.  
  163. {Get the current port and save it, then rotate stuff and restore it.}
  164.     
  165.     GetPort(oldPort);
  166.     SetPort(theGPort);
  167.  
  168.     RotateStuff(prRsl);
  169.  
  170.     SetPort(oldPort);
  171.  
  172.  END;    {**  DrawStuff  **}
  173.  
  174.  
  175. {*------ GetBestRsl -----------------------------------------------------------------*}
  176.  
  177. {**
  178.  **      GetBestRsl determines the best "square" resolution supported by the printer.
  179.  **        For example, 300 dpi horizontal by 300 dpi vertical.  It isn't necessary to
  180.  **        use square resolutions, but it generally proves easier.  We use PrGeneral and
  181.  **        the getRslDataOp opCode to get a list of the supported resolutions for our
  182.  **        printer.  Then we just go through the rgRslRec and find the maximum square
  183.  **        resolution for discrete or non-discrete data, whichever we have.  Finally, we
  184.  **        make sure it's divisible by 72 for cleaner scaling.
  185.  **}
  186.  
  187. {$S Main}
  188. FUNCTION GetBestRsl :Integer;
  189.  
  190. VAR
  191.         err            : OSErr;
  192.         theRes, num    : Integer;
  193.         getRslData    : TGetRslBlk;
  194.  
  195. BEGIN
  196.  
  197. {Start off with our maximum resolution at 0, then call PrGeneral and parse our list
  198.  of returned values.}
  199.  
  200.         theRes := 0;
  201.         getRslData.iOpCode := getRslDataOp;
  202.  
  203.         PrGeneral(@getRslData);
  204.         err := getRslData.iError;
  205.  
  206. {If our printer only supports discrete resolutions, find the largest square one and
  207.  use that.  If our printer supports a range of resolutions, choose the smaller of the
  208.  maximum X and Y resolutions, then make it divisible by 72 for cleaner scaling.}
  209.  
  210.         IF (err = noErr) THEN
  211.             IF (getRslData.XRslRg.iMax = 0) AND (getRslData.YRslRg.iMax = 0) THEN
  212.                 BEGIN                                                                {Discrete resolutions.}
  213.                     FOR num := 1 TO getRslData.iRslRecCnt DO
  214.                         IF (getRslData.rgRslRec[num].iXRsl = getRslData.rgRslRec[num].iYRsl)
  215.                             AND (theRes < getRslData.rgRslRec[num].iXRsl) THEN
  216.                                 theRes := getRslData.rgRslRec[num].iXRsl;
  217.                 END
  218.             ELSE
  219.                 BEGIN                                                                {Variable resolutions.}
  220.                     IF (getRslData.XRslRg.iMax < getRslData.YRslRg.iMax) THEN
  221.                         theRes := (getRslData.XRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. X resolution.}
  222.                     ELSE
  223.                         theRes := (getRslData.YRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. Y resolution.}
  224.                 END;
  225.             
  226.  
  227. {In the unlikely event that PrGeneral fails and theRes is still 0, set it to 72.
  228.  This most likely is a supported resolution.  Finally return the best resolution we
  229.  could find.}
  230.  
  231.         IF theRes = 0 THEN theRes := 72;
  232.         GetBestRsl := theRes;
  233.  
  234. END;    {**  GetBestRsl  **}
  235.  
  236.  
  237. {*------ PrintStuff ----------------------------------------------------------------*}
  238. {**
  239.  **        PrintStuff will call all of the necessary Print Manager calls to print 
  240.  **        a document. It checks PrError after each Print Manager call. If an error 
  241.  **     is found, all of the Print Manager open calls (i.e. PrOpen, PrOpenDoc...) 
  242.  **        will have a corresponding close call before the error is posted to the user. 
  243.  **        You want to use this approach to make sure the Print Manager closes properly 
  244.  **        and all temporary memory is released.
  245.  **}
  246.  
  247. {$S Main}
  248. PROCEDURE PrintStuff;
  249.  
  250. VAR
  251.     Loop,
  252.     NumberOfPages,
  253.     PageNumber        : Integer;
  254.     PrintError        : LongInt;
  255.     oldPort          : GrafPtr;
  256.     thePrRecHdl        : THPrint;
  257.     thePrPort        : TPPrPort;
  258.     theStatus        : TPrStatus;
  259.     errStr            : Str255;
  260.     rslData            : TSetRslBlk;
  261.     err                : OSErr;
  262.     prRsl            : Integer;
  263.     setMHdl            : MenuHandle;
  264.     mark            : Char;
  265.     bestRsl            : Integer;
  266.  
  267. BEGIN
  268.  
  269.  
  270. {Get our current port and create a print handle.  If no errors,
  271.  do our PrOpen call and, if no errors again, get the default
  272.  settings for the current driver.}
  273.  
  274.     GetPort(oldPort);
  275.     thePrRecHdl := THPrint(NewHandle(sizeof(TPrint)));
  276.     
  277.     IF (MemError = noErr) THEN
  278.     BEGIN
  279.         PrOpen;
  280.         IF (PrError = noErr) THEN
  281.         BEGIN
  282.             PrintDefault(thePrRecHdl);
  283.  
  284.             bestRsl := GetBestRsl;
  285.             rslData.iOpCode := SetRslOp;
  286.             rslData.hPrint := thePrRecHdl;
  287.             rslData.iXRsl := bestRsl;
  288.             rslData.iYRsl := bestRsl;
  289.             PrGeneral(@rslData);
  290.             err := rslData.iError;
  291.     
  292.             prRsl := bestRsl;
  293.  
  294.  
  295. {If we still have no errors, give style and print job dialogs, then open a
  296.  document and its page.  Keep checking for those dang printer errors.}
  297.  
  298.             IF (PrError = noErr) THEN
  299.             BEGIN
  300.                 IF (PrStlDialog(thePrRecHdl)) THEN
  301.                 BEGIN
  302.                     IF (PrJobDialog(thePrRecHdl)) THEN 
  303.                     BEGIN
  304.                         thePrPort := PrOpenDoc(thePrRecHdl, NIL, NIL);
  305.                                
  306.                         IF (PrError = noErr) THEN
  307.                         BEGIN
  308.                             PrOpenPage(thePrPort, NIL);
  309.  
  310.  
  311. {If we're still running error-free, draw our test page.  prRsl is the
  312.  resolution of our printer port.}
  313.  
  314.                             IF (PrError = noErr) THEN
  315.                                 DrawStuff(GrafPtr(thePrPort), prRsl);
  316.  
  317.  
  318. {When done, close our page and document and spool the document if necessary.  When
  319.  finshed, call PrClose to end the whole shabang.}
  320.  
  321.                             PrClosePage(thePrPort);
  322.                         END;
  323.                              
  324.                         PrCloseDoc(thePrPort);
  325.                              
  326.                         IF (thePrRecHdl^^.prJob.bJDocLoop = bSpoolLoop) and (PrError = noErr) THEN
  327.                             PrPicFile(thePrRecHdl, NIL, NIL, NIL, theStatus);
  328.                     END;
  329.                 END;
  330.             END;
  331.         END;
  332.         
  333.         PrClose;
  334.  
  335.     END;
  336.  
  337. END;    {**  PrintStuff  **}
  338.  
  339.  
  340. {*------ Initialize ----------------------------------------------------------------*}
  341. {**
  342.  **        Initialize just handles necessary Toolbox initializing, setting our quitting 
  343.  **        flag to FALSE and installing our menus.
  344.  **}
  345.  
  346. {$S Initialize}
  347. PROCEDURE Initialize;
  348.  
  349. VAR
  350.     menuBar    : Handle;
  351.     
  352. BEGIN
  353.  
  354.     InitGraf(@thePort);
  355.     InitFonts;
  356.     InitWindows;
  357.     InitMenus;
  358.     TEInit;
  359.     InitDialogs(NIL);
  360.     InitCursor;
  361.     FlushEvents(everyEvent, 0);    
  362.  
  363.     gQuitting := FALSE;
  364.  
  365.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  366.     IF (menuBar = NIL) THEN ExitToShell;    {should do real error stuff here.}
  367.     SetMenuBar(menuBar);                    {install menus}
  368.     DisposHandle(menuBar);
  369.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  370.     DrawMenuBar;
  371.  
  372. END;    {**  Initialize  **}
  373.  
  374.  
  375. {$S _DataInit}
  376. PROCEDURE _DataInit; EXTERNAL;
  377.  
  378. {This routine is automatically linked in by the MPW Linker. This external
  379.  reference to it is done so that we can unload its segment, %A5Init.}
  380.  
  381.  
  382. {*------ DoMenuCommand ----------------------------------------------------------------*}
  383. {**
  384.  **        DoMenuCommand is called when an item is chosen from the menu bar (after calling 
  385.  **        MenuSelect or MenuKey).  It does the right thing for each command.
  386.  **}
  387.  
  388. {$S Main}
  389. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  390.  
  391. VAR
  392.     menuID, menuItem    : INTEGER;
  393.     daRefNum            : INTEGER;
  394.     daName                : Str255;
  395.  
  396. BEGIN
  397.  
  398. {Get the menu ID and item ID.}
  399.  
  400.     menuID := HiWrd(menuResult);
  401.     menuItem := LoWrd(menuResult);
  402.  
  403.     CASE menuID OF
  404.         mApple:
  405.             CASE menuItem OF
  406.                 iAbout:                {bring up alert for About}
  407.                     (* We do nothing here... *);
  408.  
  409.                 OTHERWISE
  410.                 BEGIN        {all non-About items in this menu are DAs}
  411.                     GetItem(GetMHandle(mApple), menuItem, daName);
  412.                     daRefNum := OpenDeskAcc(daName);
  413.                 END;
  414.             END;
  415.  
  416.         mFile:                            {File Menu}
  417.             CASE menuItem OF
  418.                 iPrint:                        {-> Print Test Page.}
  419.                     PrintStuff;
  420.                 iQuit:
  421.                     gQuitting := TRUE;        {-> Quit}
  422.             END;
  423.     END;        
  424.  
  425.     HiliteMenu(0);
  426.  
  427. END;    {**  DoMenuCommand  **}
  428.  
  429.  
  430. {*------ DoEvent ----------------------------------------------------------------*}
  431. {**
  432.  **        DoEvent handles incoming events for our app.  In this skimpy sample, we
  433.  **        only handle menu events and system clicks.
  434.  **}
  435.  
  436. {$S Main}
  437. PROCEDURE DoEvent;
  438.  
  439. VAR
  440.     part        : INTEGER;
  441.     key            : Char;
  442.     quitting    : Boolean;
  443.     event        : EventRecord;
  444.     window        : WindowPtr;
  445.  
  446. BEGIN
  447.  
  448. {Repeatedly handle menu selecting events until our quit flag is set.}
  449.  
  450.     REPEAT
  451.         BEGIN
  452.             SystemTask;                                    {This must be called if using GetNextEvent}
  453.  
  454.             IF (GetNextEvent(everyEvent, event)) THEN
  455.                 CASE event.what OF
  456.                     mouseDown:
  457.                         BEGIN
  458.                             part := FindWindow(event.where, window);
  459.                             CASE part OF
  460.                                 inMenuBar:
  461.                                     DoMenuCommand(MenuSelect(event.where));
  462.                 
  463.                                 inSysWindow:
  464.                                     SystemClick(event, window);
  465.                             END;
  466.                         END;
  467.     
  468.                     keyDown, autoKey:
  469.                         BEGIN
  470.                             key := CHR(BAnd(event.message, charCodeMask));
  471.                             IF (BAnd(event.modifiers, cmdKey) <> 0) AND (event.what = keyDown) THEN
  472.                                 DoMenuCommand(MenuKey(key));
  473.                         END;
  474.                 END;
  475.         END;
  476.     UNTIL gQuitting;
  477.  
  478. END;    {**  DoEvent  **}
  479.  
  480.  
  481. {*------ Main ----------------------------------------------------------------*}
  482. {**
  483.  **        Main kickstarts our app.
  484.  **}
  485.  
  486. {$S Main}
  487. BEGIN
  488.  
  489.     UnloadSeg(@_DataInit);    {note that _DataInit must not be in Main!}
  490.     MaxApplZone;            {expand the heap so code segments load at the top}
  491.     Initialize;                {initialize the program}
  492.     UnloadSeg(@Initialize);    {note that Initialize must not be in Main!}
  493.     DoEvent;                {handle menu events until quitting.}
  494.  
  495. END.    {**  Rotn Objects.  **}
  496.  
  497.